package com.fanrui.code.chess.mahjong;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.lang3.RandomUtils;

import com.fanrui.code.chess.AbstractChessGameTable;
import com.fanrui.code.chess.ChessTableManager;
import com.fanrui.code.chess.IChessGameType;
import com.fanrui.code.chess.mahjong.MahjongRules.CurrencyType;
import com.fanrui.code.chess.mahjong.round.MahjongOperationResponse;
import com.fanrui.code.chess.mahjong.round.MahjongRound;
import com.fanrui.code.chess.mahjong.round.MahjongTouchRound;
import com.fanrui.code.dao.EParameterType;
import com.fanrui.code.dao.ParameterConfigManager;
import com.fanrui.code.dao.TableLogDao;
import com.fanrui.code.entity.TableLog;
import com.fanrui.code.mahjong.util.MahjongCardUtil;
import com.fanrui.code.player.Player;
import com.fanrui.code.player.Player.OperationType;
import com.fanrui.code.player.PlayerDao;
import com.fanrui.code.util.FCDTools;
import com.fanrui.code.util.FCD_Point;
import com.fanrui.code.util.PlayerData;
import com.fanrui.code.util.PlayerLogUtil;
import com.fanrui.code.util.PlayerLogUtil.PlayerOpertionType;
import com.fanrui.code.util.logger.Stdout;
import com.fanrui.proto.cmd.CreateRoomPayType;
import com.fanrui.proto.cmd.FanJing;
import com.fanrui.proto.cmd.GuiPai;
import com.fanrui.proto.cmd.MaMahjong;
import com.fanrui.proto.cmd.MahjongBaseRoomInfo;
import com.fanrui.proto.cmd.MahjongIncomeResult;
import com.fanrui.proto.cmd.MahjongRoomInfo;
import com.fanrui.proto.cmd.MahjongStep;
import com.fanrui.proto.cmd.MahjongType;
import com.fanrui.proto.cmd.NotifyDealMahjongToEveryone;
import com.fanrui.proto.cmd.NotifyMahjongEndResult;
import com.fanrui.proto.cmd.NotifyMahjongGameBegin;
import com.fanrui.proto.cmd.NotifyPlayerDrawMahjong;
import com.fanrui.proto.cmd.NotifyPlayerPrepare;
import com.fanrui.proto.cmd.NotifyTotalResult;
import com.fanrui.proto.cmd.NotityOtherPlayerEnterMahjongDesk;
import com.fanrui.proto.cmd.NotityPlayerLeaveMahjongDesk;
import com.fanrui.proto.cmd.Operation;
import com.fanrui.proto.cmd.ResJoinMahjongRoom.Result;
import com.fanrui.proto.cmd.ResPlayerLeaveMahjongDesk;
import com.fanrui.proto.cmd.TotalResultItemInfo;
import com.fanrui.proto.cmd.WannengType;
import com.google.gson.Gson;

public abstract class AbstractMahjongTable<CHESSGAMETYPE extends IChessGameType> extends AbstractChessGameTable<Mahjong, MahjongGamer, MahjongRules, CHESSGAMETYPE> {

	protected boolean dissolveState = false;
	
	/**
	 * 骰子
	 */
	protected int dice1 = 0;
	protected int dice2 = 0;
	
	/**
	 * 是否开始游戏
	 */
	protected boolean isDiss = false;
	
	/**
	 * 是否有人胡牌
	 */
	private boolean haveHu = false;
	
	/**
	 * 游戏阶段
	 */
	protected MahjongStep step = MahjongStep.Wait;
	
	/**
	 * 当前正在执行的回合
	 */
	private volatile MahjongRound currentRound;
	
	/**
	 * 下一个摸牌的玩家
	 */
	private Long touchPlayerId = null;
	
	/**
	 * 已经打出的牌 最后一张就是最新一张被打出的牌
	 */
	private Map<Long, List<Mahjong>> sendCards = new HashMap<Long, List<Mahjong>>();
	
	private Map<Long, List<Integer>> totalResult = new HashMap<Long, List<Integer>>();
	
	/**
	 * 出牌次数
	 */
	private int sendCardNum = 0;
	
	/**
	 * 摸牌次数
	 */
	private int touchCardNum = 0;
	
	private List<MahjongRound> rounds;
	
	/**
	 * 是否在杠上花阶段
	 */
	private Long gangId = null;
	private MahjongIncome lastGangIncome = null;
	
	/**
	 * 最后打出的牌
	 */
	public volatile Mahjong lastSendCard;
	
	/**
	 * 战绩记录
	 */
	protected MahjongStandings mahjongStandings;
	
	/**
	 * 精牌
	 */
	protected Map<Mahjong, WannengType> wannengMahjongs = new HashMap<Mahjong, WannengType>();
	
	/**
	 * 买马
	 */
	protected List<MaMahjong.Builder> maMahjongs = new ArrayList<MaMahjong.Builder>();
	
	public abstract Collection<Long> needLeavePlayers();
	
	public AbstractMahjongTable(int ID, MahjongRules gameRules, CHESSGAMETYPE chessTableType) {
		super(ID, gameRules, chessTableType);
	}

	public Long getTouchPlayerId() {
		return touchPlayerId;
	}

	public void setTouchPlayerId(Long touchPlayerId) {
		this.touchPlayerId = touchPlayerId;
	}

	public boolean isHaveHu() {
		return haveHu;
	}

	public void setHaveHu(boolean haveHu) {
		this.haveHu = haveHu;
	}

	public Long getGangId() {
		return gangId;
	}

	public MahjongIncome getLastGangIncome() {
		return lastGangIncome;
	}

	public void setLastGangIncome(MahjongIncome lastGangIncome) {
		this.lastGangIncome = lastGangIncome;
	}
	
	public void clearGang() {
		this.gangId = null;
		setLastGangIncome(null);
	}
	
	public MahjongRound getCurrentRound() {
		return currentRound;
	}
	
	public MahjongStep getStep() {
		return step;
	}
	
	public Mahjong getLastSendCard() {
		return lastSendCard;
	}

	public void setLastSendCard(Mahjong lastSendCard) {
		this.lastSendCard = lastSendCard;
	}

	public List<MaMahjong> getMaMahjongsBuild() {
		List<MaMahjong> result = new ArrayList<MaMahjong>();
		if(maMahjongs != null && !maMahjongs.isEmpty()) {
			for(MaMahjong.Builder mm : maMahjongs) {
				result.add(mm.build());
			}
		}
		return result;
	}
	
	public List<MaMahjong.Builder> getMaMahjongs() {
		return maMahjongs;
	}

	public void setMaMahjongs(List<MaMahjong.Builder> maMahjongs) {
		this.maMahjongs = maMahjongs;
	}
	
	public void addMa(MaMahjong.Builder ma) {
		this.maMahjongs.add(ma);
	}
	
	@Override
	public boolean startDeductionCreateRoomCost() {
		CreateRoomPayType payType = this.getGameRules().getRoomConfig().getPayType();
		if(payType == CreateRoomPayType.WinnerPay) {
			return true;
		}
		// 开放消耗
		long cost = ParameterConfigManager.getInstance().getIntParameter(EParameterType.CreateTableConsumption) * this.getGameRules().getRoomConfig().getBoardNum();
		Player costPlayer;
		//AA
		if(payType == CreateRoomPayType.AAPay) {
			cost = Long.parseLong(cost/this.getGamers().size()+"");
			
			for (MahjongGamer gamer : this.getGamers().values()) {
				costPlayer = PlayerDao.getInstance().findById(gamer.getPlayerID());
				if(costPlayer.getGold() <= cost) {
					return false;
				}
			}
			for (MahjongGamer gamer : this.getGamers().values()) {
				costPlayer = PlayerDao.getInstance().findById(gamer.getPlayerID());
				PlayerData costPlayerData = new PlayerData(costPlayer);
				costPlayer.operationCost(com.fanrui.proto.cmd.CurrencyType.C_Gold, cost, OperationType.Minus);
				PlayerLogUtil.log(costPlayer, PlayerOpertionType.CreateFriendRoom, null, null, null, cost, null, null, "创建麻将房间AA", costPlayerData);
				costPlayer.save();
			}
		}
		//ALL
		if(payType == CreateRoomPayType.RoomerPay) {
			costPlayer = PlayerDao.getInstance().findById(bankID);
			if(costPlayer.getGold() <= cost) {
				return false;
			}
			PlayerData costPlayerData = new PlayerData(costPlayer);
			costPlayer.operationCost(com.fanrui.proto.cmd.CurrencyType.C_Gold, cost, OperationType.Minus);
			PlayerLogUtil.log(costPlayer, PlayerOpertionType.CreateFriendRoom, null, null, null, cost, null, null, "创建麻将房间ALL", costPlayerData);
			costPlayer.save();
		}
		return true;
	}

	@Override
	public synchronized void startGame() {
		// 第一局游戏开始扣房费
		if(roundNum == 0 && this.getGameRules().getCurrencyType() == CurrencyType.Score) {
			if(!startDeductionCreateRoomCost()) {
				//TODO 通知金币不足
				return;
			}
		}
		
		isDiss = true;
		if (!gameRules.checkBoard(roundNum)) {
			return;
		}
		if (step != MahjongStep.Wait) {
			return;
		}
		step = MahjongStep.Do;
		roundNum++;
		Stdout.GET().info("麻将牌桌：" + getID() + "开始游戏.当前局数：" + roundNum);
		dice1 = RandomUtils.nextInt(1, 7);
		dice2 = RandomUtils.nextInt(1, 7);
		// 配牌
		cards = MahjongCardUtil.getInstance().createModelCard(gameRules, MahjongCardUtil.getInstance().clone(backupCards));
		
		// 配置万能牌
		notifyWannengMahjongs();
		// 发牌
		deal();
		mahjongStandings = new MahjongStandings(getID(), gamers.values(), Mahjong.clone(cards));
		// 开始一个回合
		//延迟摸牌，前端发牌效果
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				startRound(null);
			}
		}, 10000);
	}
	
	@Override
	protected void deal() {
		super.deal();
//		for (MahjongGamer gamer : gamers.values()) {
//			for (Mahjong mahjong : gamer.getHandCards()) {
//				if(mahjong != null && mahjong.getWannengType() != null) {
//					gamer.addJing(mahjong.getWannengType());
//				}
//			}
//		}
	}

	/**
	 * 添加鬼牌
	 */
	public void addGui() {
		GuiPai guipai = gameRules.getRoomConfig().getGuiPai();
		if(guipai == GuiPai.NotGui) {
			return;
		}else if(guipai == GuiPai.BaiBanAsGui) {
			wannengMahjongs.put(new Mahjong(0, 7, MahjongType.SAN, WannengType.Gui), WannengType.Gui);
		}else if(guipai == GuiPai.DoubleGui) {
			Mahjong jiaGui = this.cards.get(cards.size()-20);
			Mahjong zhenGui = jiaGui.getNextMahjong();
			zhenGui.setWannengType(WannengType.Gui);
			wannengMahjongs.put(jiaGui, null);
			wannengMahjongs.put(zhenGui, WannengType.Gui);
		}
	}
	
	/**
	 * 添加精牌
	 */
	public void addJing() {
		synchronized (wannengMahjongs) {
			FanJing fanjing = gameRules.getRoomConfig().getFanJing();
			int jingNumber = 0;
			if(fanjing == FanJing.UpDown) {
				jingNumber = 2;
			}else if(fanjing == FanJing.UpDownLeftRight) {
				jingNumber = 4;
			}else if(fanjing == FanJing.UpDownLeftLeftRightRight) {
				jingNumber = 6;
			}
			int cardIndex = 1;
			
			for (int i = 1; i <= jingNumber; i++) {
				if(cardIndex >= cards.size()) {
					cardIndex = 1;
				}
				Mahjong zheng = this.cards.get(cards.size()-cardIndex);//正精
				Mahjong fu = zheng.getNextMahjong();//副精
				
				boolean isCreate = true;
				while(isCreate) {//循环产生不重复的精牌
					if(wannengMahjongs != null && !wannengMahjongs.isEmpty()) {
						for(Mahjong mj : wannengMahjongs.keySet()) {
							if(zheng.cardEquals(mj) || fu.cardEquals(mj)) {
								isCreate = false;
								cardIndex++;
								zheng = this.cards.get(cards.size()-cardIndex);//正精
								fu = zheng.getNextMahjong();//副精
								break;
							}
						}
						isCreate = !isCreate;
					}else {
						break;
					}
				}
				cardIndex++;
				
				zheng = zheng.cloneCard();
				fu = fu.cloneCard();
				if(zheng != null && fu != null) {
					WannengType zhengType = null, fuType = null;
					if(i == 1) {
						zhengType = WannengType.ShangZheng;
						fuType = WannengType.ShangFu;
					}else if(i == 2) {
						zhengType = WannengType.XiaZheng;
						fuType = WannengType.XiaFu;
					}else if(i == 3) {
						zhengType = WannengType.ZuoZheng;
						fuType = WannengType.ZuoFu;
					}else if(i == 4) {
						zhengType = WannengType.YouZheng;
						fuType = WannengType.YouFu;
					}else if(i == 5) {
						zhengType = WannengType.ZuozuoZheng;
						fuType = WannengType.ZuozuoFu;
					}else if(i == 6) {
						zhengType = WannengType.YouyouZheng;
						fuType = WannengType.YouyouFu;
					}
					zheng.setWannengType(zhengType);
					fu.setWannengType(fuType);
					wannengMahjongs.put(zheng, zhengType);
					wannengMahjongs.put(fu, fuType);
					
					// 初始化玩家的精牌
					for (MahjongGamer gamer : this.gamers.values()) {
						gamer.initJing(zhengType);
						gamer.initJing(fuType);
					}
				}
			}
		}
	}
	
	private void addMaMahjongs() {
		int maNumber = gameRules.getRoomConfig().getBuyMa();
		for(int i = 1; i <= maNumber; i++) {
			MaMahjong.Builder ma = MaMahjong.newBuilder();
			ma.setMj(cards.get(cards.size()-i).toCardCmd());
			ma.setIsWinning(false);
			maMahjongs.add(ma);
		}
	}
	
	/**
	 * 通知万能牌
	 */
	public void notifyWannengMahjongs() {
		if(gameRules instanceof GanZhouMahjongRules) { //赣州麻将精牌
			addJing();
		}
		if(gameRules instanceof BaoHuRules) { //爆胡麻将鬼牌
			addGui();
			addMaMahjongs();
		}
		for(Mahjong mj : wannengMahjongs.keySet()) {
			if(wannengMahjongs.get(mj) != null) {
				for(Mahjong card : cards) {
					if(mj.cardEquals(card)) {
						card.setWannengType(wannengMahjongs.get(mj));
					}
				}
			}
		}
	}
	
	/**
	 * 通知玩家游戏开始
	 */
	public void notifyBegin() {
		NotifyMahjongGameBegin.Builder notify = NotifyMahjongGameBegin.newBuilder();
		notify.setDice1(RandomUtils.nextInt(1, 7));
		notify.setDice2(RandomUtils.nextInt(1, 7));
		notify.setBanker(bankID);
		broadcast(notify.build());
	}
	
	@Override
	public void havePlayerReady(MahjongGamer gamer) {
		NotifyPlayerPrepare.Builder builder = NotifyPlayerPrepare.newBuilder();
		builder.setUserID(gamer.getPlayerID());
		broadcast(builder.build());
	}

	@Override
	public String tableInfo() {
		if (mahjongStandings == null) {
			return new Gson().toJson(this);
		} else {
			return mahjongStandings.toJson();
		}
	}

	@Override
	public List<Mahjong> createCard() {
		return MahjongCardUtil.getInstance().createMahjongCard(getGameRules(),true);
	}

	@Override
	public Result checkEnterCondition(Player player, boolean isReconnect) {
		if(isReconnect) {
			return Result.Success;
		}
		if (isFull() && !isReconnect) {
			return Result.Filled;
		}
		if(this.getGamers() != null && !this.getGamers().isEmpty()) {
			for (MahjongGamer mg : this.getGamers().values()) {
				long softDistance = this.getGameRules().getRoomConfig().getSafeDistance() * 1000;
				if(softDistance > 0) { //安全距离检测
					double distance = FCDTools.Distance(new FCD_Point(player.getX(), player.getY()), new FCD_Point(mg.getX(), mg.getY()));
					if(distance <= softDistance) {
						return Result.LocationWarning;
					}
				}
				if(this.getGameRules().getRoomConfig().getSameIP()) { //相同IP检测
					if(player.getIp().equals(mg.getIp())) {
						return Result.IPwarning;
					}
				}
			}
		}
		return Result.Success;
	}

	@Override
	public MahjongGamer findOrCreateGamer(Player player) {
		return null;
	}

	@Override
	public void notifyPlayerEnter(MahjongGamer gamer) {
		// 通知其他人有人进入
		NotityOtherPlayerEnterMahjongDesk.Builder notify = NotityOtherPlayerEnterMahjongDesk.newBuilder();
		notify.setGamer(gamer.toCmd());
		broadcast(notify.build());
		gamer.setSeatNo(getGamerSeatNO(gamer.getPlayerID()));
	}

	@Override
	public void enter(boolean isReconnect, MahjongGamer gamer) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean isGaming() {
		return step != MahjongStep.Wait;
	}

	@Override
	public ResPlayerLeaveMahjongDesk.Result checkExitCondition(Player player) {
		if ((roundNum == 0 && step == MahjongStep.Wait) || dissolveState || !gameRules.checkBoard(roundNum)) {
			return ResPlayerLeaveMahjongDesk.Result.Success;
		} else {
			return ResPlayerLeaveMahjongDesk.Result.InGaming;
		}
	}

	@Override
	public void notifyPlayerExit(MahjongGamer gamer,NotityPlayerLeaveMahjongDesk.Result exitCode) {
		if (gamer == null) {
			return;
		}
		if (bankID != null && gamer.getPlayerID() == bankID) {
			setBankID(null);
		}

		NotityPlayerLeaveMahjongDesk.Builder notify = NotityPlayerLeaveMahjongDesk.newBuilder();
		notify.setUserID(gamer.getPlayerID());
		notify.setTag(exitCode);
		broadcast(notify.build());
		gamer.send(notify.build());
	}

	@Override
	public int getDealNum(long playerID) {
		return playerID == bankID ? gameRules.gamerHandleNum() + 1 : gameRules.gamerHandleNum();
	}

	@Override
	public void notifyDealCard(MahjongGamer gamer) {
		NotifyDealMahjongToEveryone.Builder builder = NotifyDealMahjongToEveryone.newBuilder();
		builder.addAllMjs(MahjongCardUtil.getInstance().toMahjongs(gamer.getHandCards()));
		builder.setDice1(dice1);
		builder.setDice2(dice2);
		builder.setBanker(bankID);
		builder.addAllMahjongs(MahjongCardUtil.getInstance().toMahjongs(getWannengMahjongs()));
		if(cards != null) {
			builder.setSurplusMahjongNum(cards.size());
		}
		gamer.send(builder.build());
	}

	@Override
	public MahjongRoomInfo toCmd() {
		MahjongRoomInfo.Builder result = MahjongRoomInfo.newBuilder();
		result.setRoomInfo(toBaseInfo());
		result.addAllGamers(MahjongGamer.toGamers(gamers.values()));
		if (getCurrentRound() != null && getCurrentRound().getCurrentId() != null) {
			result.setOperatedPersonID(getCurrentRound().getCurrentId());
			// 如果在抢杠胡阶段 这个值传抢杠的那张牌
//			if (getCurrentRound() instanceof MahjongQiangGangHuRound) {
//				Mahjong card = ((MahjongQiangGangHuRound) getCurrentRound()).getOperationCard();
//				if (card != null) {
//					result.setCurPopMahjong(card.toCardCmd());
//				}
//			} else if (lastSendCard != null) {
////				result.setCurPopMahjong(getNewSendCard(getCurrentRound().getCurrentId()).toCardCmd());
//				result.setCurPopMahjong(lastSendCard.toCardCmd());
//			}
			if (lastSendCard != null) {
				result.setCurPopMahjong(lastSendCard.toCardCmd());
			}
		}
		result.setCurRound(roundNum);
		result.setSurplusTime(0);
		if (cards != null) {
			result.setSurplusMahjongNum(cards.size());
		}
		if (getStep() != null) {
			result.setStep(getStep());
		}
		result.setDice1(this.dice1);
		result.setDice2(this.dice2);
		return result.build();
	}
	
	public MahjongBaseRoomInfo toBaseInfo() {
		MahjongBaseRoomInfo.Builder builder = MahjongBaseRoomInfo.newBuilder();
		builder.setRoomID(getID());
		builder.setBaseRoomInfo(gameRules.getRoomConfig());
		builder.setMaxPerson(gameRules.getRoomConfig().getPlayerNumber());
		builder.setCurPerson(gamers.size());
		otherBuilder(builder);
		return builder.build();
	}
	
	public abstract void otherBuilder(MahjongBaseRoomInfo.Builder builder);

	public com.fanrui.proto.cmd.ResPlayerOperation.Result addPlayerResponse(MahjongOperationResponse response) {
		if (currentRound != null) {
			return currentRound.addResponse(response);
		}
		return com.fanrui.proto.cmd.ResPlayerOperation.Result.Failed;
	}

	/**
	 * 玩家成功的响应了一次 void
	 */
	public void playerSuccessCallResponse() {
		if (currentRound != null) {
			currentRound.doResponse();
		}
	}
	
	/**
	 * 判断玩家是否还存活（还能进行游戏）
	 * 
	 * @return boolean
	 */
	public boolean checkPlayerSurvival(MahjongGamer gamer) {
		if (gamer.isHu()) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 查找还能进行游戏的玩家
	 * 
	 * @param excludeIds
	 * @return Collection<Long>
	 */
	public Collection<Long> getSurvivalPlayers(long... excludeIds) {
		ArrayList<Long> result = new ArrayList<Long>();
		synchronized (gamers) {
			for (MahjongGamer gamer : gamers.values()) {
				if (excludeIds != null && checkIn(excludeIds, gamer.getPlayerID())) {
					continue;
				}
				if (!checkPlayerSurvival(gamer)) {
					continue;
				}
				result.add(gamer.getPlayerID());
			}
		}
		return result;
	}
	
	/**
	 * 查找还能进行游戏的玩家
	 * 
	 * @param excludeIds
	 * @return Collection<Long>
	 */
	public List<Long> getAllPlayersId() {
		ArrayList<Long> result = new ArrayList<Long>();
		synchronized (gamers) {
			for (MahjongGamer gamer : gamers.values()) {
				result.add(gamer.getPlayerID());
			}
		}
		return result;
	}

	public boolean checkIn(long[] array, long key) {
		if (array == null) {
			return false;
		}
		for (long element : array) {
			if (element == key) {
				return true;
			}
		}
		return false;
	}

	public void startRound(Long gangId) {
		step = MahjongStep.Do;
		if(this.gameRules instanceof BaoHuRules) { //爆胡最后几张牌为马
			if(cards.size() <= this.gameRules.getRoomConfig().getBuyMa() || isHaveHu()) {
				// 结束游戏
				end();
				return;
			}
		}else if (cards.isEmpty() || isHaveHu()) {// 牌摸完了
			// 结束游戏
			end();
			return;
		}
		Mahjong card = null;
		if (touchPlayerId == null) {
			touchPlayerId = bankID;
			if(touchPlayerId == null) {
				return;
			}
			MahjongGamer gamer = getGamerByPlayerID(touchPlayerId);
			card = gamer.getHandCards().get(13);
		} else {
			MahjongGamer gamer = getGamerByPlayerID(touchPlayerId);
//			if (gamer.canReples()) {
//				MahjongCardUtil.getInstance().swap(cards, gamer.getReplesCard());
//			}
			card = cards.remove(0);
//			if(card.getWannengType() != null) {
//				gamer.addJing(card.getWannengType());
//			}
			gamer.addGroup(Arrays.asList(card), Operation.TOUCH);
			gamer.addCard(card);
			gamer.setGuoShouHu(null);
			NotifyPlayerDrawMahjong.Builder notify = NotifyPlayerDrawMahjong.newBuilder();
			notify.setMj(card.toCardCmd());
			notify.setUserID(gamer.getPlayerID());
			notify.setMjNum(cards.size());
			broadcast(notify.build());
			touchCardNum++;
		}
		this.gangId = gangId;
		if (gangId == null) {
			setLastGangIncome(null);
		}
		MahjongRound round = new MahjongTouchRound(touchPlayerId, this, card, gangId);
		setCurrentRound(round);
	}

	public void setCurrentRound(MahjongRound currentRound) {
		this.currentRound = currentRound;
		if (currentRound != null) {
			currentRound.before();
		}
	}
	
	/**
	 * 结束
	 */
	public void end() {
		NotifyMahjongEndResult.Builder notify = NotifyMahjongEndResult.newBuilder();
		synchronized (gamers) {
			for (MahjongGamer gamer : gamers.values()) {
				MahjongIncomeResult result = gamer.toMahjongIncomeResult();
				notify.addResults(result);
				// 记录战绩
				try {
					addTableLog(gamer.getPlayerID(), gamer.getNickName(), mahjongStandings.getId(), result.getTotalIncome(), result.toString());
				} catch (Exception e) {
					e.printStackTrace();
				}
				List<Integer> incomes = null;
				if (totalResult.containsKey(gamer.getPlayerID())) {
					incomes = totalResult.get(gamer.getPlayerID());
				} else {
					incomes = new ArrayList<Integer>();
					totalResult.put(gamer.getPlayerID(), incomes);
				}
				incomes.add(result.getTotalIncome());
				if(gamer.isHu()) {
					notify.setHuPlayer(gamer.toCmd());
				}
				gamer.reset();
			}
		}
		notify.addAllMaMahjongs(getMaMahjongsBuild());
		broadcast(notify.build());
		if (sendCards != null) {
			sendCards.clear();
		}
		if (!gameRules.checkBoard(roundNum)) {
			step = MahjongStep.Wait;
			nofityTotalResult();
			ChessTableManager.getInstance().destoryRoom(getID());
		}
		if (mahjongStandings != null) {
			MahjongStandings.addStandings(mahjongStandings);
		}

		// TODO 改成可以复用的
		if(cards != null) {
			cards.clear();
		}
		if(wannengMahjongs != null) {
			wannengMahjongs.clear();
		}
		if(maMahjongs != null) {
			maMahjongs.clear();
		}
		step = MahjongStep.Wait;
		currentRound = null;
		touchPlayerId = null;
		haveHu = false;
		sendCardNum = 0;
		mahjongStandings = null;
		dice1 = 0;
		dice2 = 0;
		touchCardNum = 0;
		sendCardNum = 0;
		lastSendCard = null;
		constraintLeave();
	}
	
	protected TableLog addTableLog(long playerId, String playerName, String logToken, int income, String incomeInfo) {
		TableLog tableLog = createTableLog(playerId, playerName, logToken, income, incomeInfo);
		if(tableLog != null) {
			TableLogDao.getInstance().save(tableLog);
		}
		return tableLog;
	}
	
	protected TableLog createTableLog(long playerId, String playerNickName, String logToken, int income,
			String incomeInfo) {
		return null;
	}

	public void constraintLeave() {
		// 提出所有离开的玩家
		Collection<Long> playerIDS = new ArrayList<Long>();
		for (MahjongGamer gamer : gamers.values()) {
			if (gamer.getLeave()) {
				playerIDS.add(gamer.getPlayerID());
			}
		}
		for (long playerID : playerIDS) {
			exit(playerID,NotityPlayerLeaveMahjongDesk.Result.GAMEOUT);
		}
		needLeavePlayers();
	}

	/**
	 * 发送总结算 void
	 */
	public void nofityTotalResult() {
		NotifyTotalResult.Builder notify = NotifyTotalResult.newBuilder();
		synchronized (gamers) {
			for (MahjongGamer gamer : gamers.values()) {
				List<Integer> incomes = totalResult.get(gamer.getPlayerID());
				TotalResultItemInfo.Builder builder = TotalResultItemInfo.newBuilder();
				builder.setGamer(gamer.toCmd());
				if (incomes != null) {
					builder.addAllIncome(incomes);
				}
				notify.addItems(builder.build());
			}
		}
		broadcast(notify.build());
	}

	/**
	 * 打出一张牌
	 * 
	 * @param card
	 */
	public void sendCard(long player, Mahjong card) {
		List<Mahjong> cards = sendCards.get(player);
		if (cards == null) {
			cards = new ArrayList<Mahjong>();
			sendCards.put(player, cards);
		}
		cards.add(card);
		sendCardNum++;
	}

	public final void broadcast(MahjongOperationResponse response, int waitTime, Collection<Long> notifyPlayerId) {
		if (notifyPlayerId != null && notifyPlayerId.size() > 0) {
			for (long playerId : notifyPlayerId) {
				MahjongGamer gamer = getGamerByPlayerID(playerId);
				if (gamer != null) {
					gamer.notifyResponse(response, waitTime);
				}
			}
		}
	}
	
	public final void broadcast(MahjongOperationResponse response, int waitTime) {
		synchronized (gamers) {
			for (MahjongGamer gamer : gamers.values()) {
				gamer.notifyResponse(response, waitTime);
			}
		}
	}

	public void addRound(MahjongRound round) {
		if (round != null) {
			if (rounds == null) {
				rounds = new ArrayList<MahjongRound>();
			}
			rounds.add(round);
			if (mahjongStandings != null) {
				mahjongStandings.addStandingsRound(round.toStandingsRound());
			}
		}
	}

	public Collection<Mahjong> getSendCards(long playerID) {
		return sendCards.get(playerID);
	}
	
	public List<Mahjong> getSendCards() {
		List<Mahjong> result = new ArrayList<Mahjong>();
		for(List<Mahjong> mjs : sendCards.values()) {
			for(Mahjong mj : mjs) {
				result.add(mj);
			}
		}
		return result;
	}

	public void removeSendCard(Mahjong card) {
		if (card != null && sendCards != null && !sendCards.isEmpty()) {
			for (List<Mahjong> list : sendCards.values()) {
				Iterator<Mahjong> iterator = list.iterator();
				while (iterator.hasNext()) {
					if (card.equals(iterator.next())) {
						iterator.remove();
					}
				}
			}
		}
	}

	/**
	 * 上一局操作人的Id
	 * @return
	 */
	public long getLastRoundGamerId(long playerId) {
		if(rounds != null && !rounds.isEmpty()) {
			if(rounds.get(rounds.size()-1) != null) {
				// 避免返回的id是操作人的id
				long id = rounds.get(rounds.size()-1).getCurrentId();
				if(playerId != id) {
					return id;
				}
			}
		}
		return 0;
	}

	public Collection<Mahjong> getWannengMahjongs() {
		return wannengMahjongs.keySet();
	}
	
	

	/**
	 * 还没有摸过牌
	 * 
	 * @return boolean
	 */
	public boolean notTouch() {
		return touchCardNum == 0;
	}
	
	public int getSendCardNum() {
		return sendCardNum;
	}

	public boolean isGang() {
		return gangId != null;
	}

}
